#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""

from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division
from __future__ import absolute_import

权限上下文适配模块
用于验证权限稳定性和检测临时提权情况
"""

import os
import subprocess
import time
import json
import logging
from typing import Dict, List, Optional, Tuple, Any
from dataclasses import dataclass, asdict
from enum import Enum
import re



try:
    import pwd
    import grp
    UNIX_MODULES_AVAILABLE = True
except ImportError:
    # Windows环境下pwd和grp模块不可用
    UNIX_MODULES_AVAILABLE = False

class PrivilegeType(Enum):
    """权限类型枚举"""
    ROOT = "root"
    SUDO = "sudo"
    SETUID = "setuid"
    SETGID = "setgid"
    CAPABILITY = "capability"
    NORMAL = "normal"

class PrivilegeStability(Enum):
    """权限稳定性枚举"""
    PERMANENT = "permanent"      # 永久权限
    TEMPORARY = "temporary"      # 临时权限
    SESSION_BASED = "session"    # 会话权限
    TIME_LIMITED = "time_limited" # 时间限制权限
    UNKNOWN = "unknown"          # 未知

@dataclass
class PrivilegeInfo:
    """权限信息数据类"""
    uid: int
    gid: int
    username: str
    groups: List[str]
    privilege_type: PrivilegeType
    stability: PrivilegeStability
    sudo_privileges: List[str]
    capabilities: List[str]
    session_info: Dict[str, Any]
    expiry_time: Optional[float] = None
    source: str = ""  # 权限来源
    
class PrivilegeContextAdapter:
    """权限上下文适配器"""
    
    def __init__(self, logger: Optional[logging.Logger] = None):
        self.logger = logger or logging.getLogger(__name__)
        self.current_privilege = None
        self.privilege_history = []
        
    def analyze_current_privilege(self) -> PrivilegeInfo:
        """分析当前权限状态"""
        try:
            if not UNIX_MODULES_AVAILABLE:
                # 非Unix/Linux系统，返回默认值
                return PrivilegeInfo(
                    uid=0,
                    gid=0,
                    username="unknown",
                    groups=[],
                    privilege_type=PrivilegeType.NORMAL,
                    stability=PrivilegeStability.UNKNOWN,
                    sudo_privileges=[],
                    capabilities=[],
                    session_info={}
                )
            
            # Unix/Linux系统
            uid = os.getuid()
            gid = os.getgid()
            username = pwd.getpwuid(uid).pw_name
            groups = [grp.getgrgid(g).gr_name for g in os.getgroups()]
            
            # 分析权限类型
            privilege_type = self._determine_privilege_type(uid, username)
            
            # 分析权限稳定性
            stability = self._analyze_privilege_stability(uid, username)
            
            # 获取sudo权限
            sudo_privileges = self._get_sudo_privileges(username)
            
            # 获取capabilities
            capabilities = self._get_capabilities()
            
            # 获取会话信息
            session_info = self._get_session_info()
            
            # 检测权限过期时间
            expiry_time = self._detect_privilege_expiry(username)
            
            # 确定权限来源
            source = self._determine_privilege_source(uid, username)
            
            privilege_info = PrivilegeInfo(
                uid=uid,
                gid=gid,
                username=username,
                groups=groups,
                privilege_type=privilege_type,
                stability=stability,
                sudo_privileges=sudo_privileges,
                capabilities=capabilities,
                session_info=session_info,
                expiry_time=expiry_time,
                source=source
            )
            
            self.current_privilege = privilege_info
            self.privilege_history.append(privilege_info)
            
            self.logger.info("当前权限分析完成: {}, 稳定性: {}".format(privilege_type.value, stability.value))
            return privilege_info
            
        except Exception as e:
            self.logger.error("权限分析失败: {}".format(e))
            raise
    
    def _determine_privilege_type(self, uid: int, username: str) -> PrivilegeType:
        """确定权限类型"""
        if uid == 0:
            return PrivilegeType.ROOT
        
        # 检查sudo权限
        if self._has_sudo_access(username):
            return PrivilegeType.SUDO
        
        # 检查setuid/setgid程序
        if self._check_setuid_context():
            return PrivilegeType.SETUID
        
        # 检查capabilities
        if self._has_capabilities():
            return PrivilegeType.CAPABILITY
        
        return PrivilegeType.NORMAL
    
    def _analyze_privilege_stability(self, uid: int, username: str) -> PrivilegeStability:
        """分析权限稳定性"""
        if uid == 0:
            # 检查是否为临时root
            if self._is_temporary_root():
                return PrivilegeStability.TEMPORARY
            return PrivilegeStability.PERMANENT
        
        # 检查sudo会话
        if self._is_sudo_session():
            return PrivilegeStability.SESSION_BASED
        
        # 检查时间限制
        if self._has_time_limit():
            return PrivilegeStability.TIME_LIMITED
        
        # 检查临时提权
        if self._is_temporary_elevation():
            return PrivilegeStability.TEMPORARY
        
        return PrivilegeStability.UNKNOWN
    
    def _get_sudo_privileges(self, username: str) -> List[str]:
        """获取sudo权限列表"""
        try:
            result = subprocess.run(
                ['sudo', '-l', '-U', username],
                capture_output=True,
                text=True,
                timeout=10
            )
            
            if result.returncode == 0:
                return self._parse_sudo_output(result.stdout)
            
        except (subprocess.TimeoutExpired, FileNotFoundError):
            pass
        
        return []
    
    def _parse_sudo_output(self, output: str) -> List[str]:
        """解析sudo -l输出"""
        privileges = []
        lines = output.split('\n')
        
        for line in lines:
            line = line.strip()
            if line.startswith('(') and ')' in line:
                # 解析 (user) command 格式
                privileges.append(line)
            elif line.startswith('/') or line.startswith('ALL'):
                # 解析命令路径
                privileges.append(line)
        
        return privileges
    
    def _get_capabilities(self) -> List[str]:
        """获取进程capabilities"""
        try:
            with open("/proc/{}/status".format(os.getpid()), 'r') as f:
                content = f.read()
            
            capabilities = []
            for line in content.split('\n'):
                if line.startswith('Cap'):
                    capabilities.append(line.strip())
            
            return capabilities
            
        except Exception:
            return []
    
    def _get_session_info(self) -> Dict[str, Any]:
        """获取会话信息"""
        session_info = {}
        
        try:
            # 获取环境变量
            session_info['env'] = {
                'SUDO_USER': os.environ.get('SUDO_USER'),
                'SUDO_UID': os.environ.get('SUDO_UID'),
                'SUDO_GID': os.environ.get('SUDO_GID'),
                'SSH_CLIENT': os.environ.get('SSH_CLIENT'),
                'SSH_CONNECTION': os.environ.get('SSH_CONNECTION'),
                'DISPLAY': os.environ.get('DISPLAY'),
            }
            
            # 获取进程信息
            session_info['ppid'] = os.getppid()
            session_info['pid'] = os.getpid()
            
            # 获取登录信息
            session_info['login_info'] = self._get_login_info()
            
        except Exception as e:
            self.logger.warning("获取会话信息失败: {}".format(e))
        
        return session_info
    
    def _get_login_info(self) -> Dict[str, Any]:
        """获取登录信息"""
        login_info = {}
        
        try:
            # 使用who命令
            result = subprocess.run(['who'], capture_output=True, text=True)
            if result.returncode == 0:
                login_info['who'] = result.stdout.strip()
            
            # 使用w命令
            result = subprocess.run(['w'], capture_output=True, text=True)
            if result.returncode == 0:
                login_info['w'] = result.stdout.strip()
            
        except Exception:
            pass
        
        return login_info
    
    def _detect_privilege_expiry(self, username: str) -> Optional[float]:
        """检测权限过期时间"""
        try:
            # 检查sudo时间戳
            sudo_timestamp = self._get_sudo_timestamp(username)
            if sudo_timestamp:
                return sudo_timestamp
            
            # 检查会话过期
            session_expiry = self._get_session_expiry()
            if session_expiry:
                return session_expiry
            
        except Exception as e:
            self.logger.warning("检测权限过期时间失败: {}".format(e))
        
        return None
    
    def _get_sudo_timestamp(self, username: str) -> Optional[float]:
        """获取sudo时间戳"""
        try:
            timestamp_file = "/var/lib/sudo/ts/{}".format(username)
            if os.path.exists(timestamp_file):
                stat = os.stat(timestamp_file)
                # sudo默认15分钟过期
                return stat.st_mtime + 900
        except Exception:
            pass
        
        return None
    
    def _get_session_expiry(self) -> Optional[float]:
        """获取会话过期时间"""
        # 这里可以根据具体环境实现
        # 例如检查SSH会话、screen会话等
        return None
    
    def _determine_privilege_source(self, uid: int, username: str) -> str:
        """确定权限来源"""
        sources = []
        
        if uid == 0:
            sources.append("root_user")
        
        if os.environ.get('SUDO_USER'):
            sources.append("sudo_from_{}".format(os.environ.get('SUDO_USER')))
        
        if os.environ.get('SSH_CLIENT'):
            sources.append("ssh_session")
        
        if self._check_setuid_context():
            sources.append("setuid_binary")
        
        return ", ".join(sources) if sources else "unknown"
    
    def _has_sudo_access(self, username: str) -> bool:
        """检查是否有sudo访问权限"""
        try:
            result = subprocess.run(
                ['sudo', '-n', '-l'],
                capture_output=True,
                text=True,
                timeout=5
            )
            return result.returncode == 0
        except Exception:
            return False
    
    def _check_setuid_context(self) -> bool:
        """检查是否在setuid上下文中"""
        try:
            # 检查当前进程的父进程
            with open("/proc/{}/stat".format(os.getppid()), 'r') as f:
                stat_line = f.read().strip()
            
            # 简单检查，实际实现可能需要更复杂的逻辑
            return 'sudo' in stat_line or 'su' in stat_line
            
        except Exception:
            return False
    
    def _has_capabilities(self) -> bool:
        """检查是否有特殊capabilities"""
        capabilities = self._get_capabilities()
        
        # 检查是否有非默认的capabilities
        for cap in capabilities:
            if 'CapEff' in cap and cap.split(':')[1].strip() != '0000000000000000':
                return True
        
        return False
    
    def _is_temporary_root(self) -> bool:
        """检查是否为临时root权限"""
        # 检查环境变量
        if os.environ.get('SUDO_USER'):
            return True
        
        # 检查进程树
        try:
            ppid = os.getppid()
            with open("/proc/{}/comm".format(ppid), 'r') as f:
                parent_comm = f.read().strip()
            
            if parent_comm in ['sudo', 'su', 'pkexec']:
                return True
                
        except Exception:
            pass
        
        return False
    
    def _is_sudo_session(self) -> bool:
        """检查是否为sudo会话"""
        return bool(os.environ.get('SUDO_USER'))
    
    def _has_time_limit(self) -> bool:
        """检查是否有时间限制"""
        if not UNIX_MODULES_AVAILABLE:
            return False
            
        # 检查sudo时间戳
        username = pwd.getpwuid(os.getuid()).pw_name
        timestamp = self._get_sudo_timestamp(username)
        
        if timestamp and timestamp > time.time():
            return True
        
        return False
    
    def _is_temporary_elevation(self) -> bool:
        """检查是否为临时提权"""
        # 综合判断
        return (self._is_temporary_root() or 
                self._is_sudo_session() or 
                self._has_time_limit())
    
    def verify_privilege_stability(self, min_duration: int = 300) -> bool:
        """验证权限稳定性"""
        if not self.current_privilege:
            self.analyze_current_privilege()
        
        privilege = self.current_privilege
        
        # 如果是永久权限，直接返回True
        if privilege.stability == PrivilegeStability.PERMANENT:
            return True
        
        # 如果是临时权限，检查剩余时间
        if privilege.expiry_time:
            remaining_time = privilege.expiry_time - time.time()
            if remaining_time < min_duration:
                self.logger.warning("权限将在 {} 秒后过期，不满足最小持续时间要求".format(int(remaining_time)))
                return False
        
        # 如果是会话权限，检查会话稳定性
        if privilege.stability == PrivilegeStability.SESSION_BASED:
            return self._verify_session_stability()
        
        return True
    
    def _verify_session_stability(self) -> bool:
        """验证会话稳定性"""
        try:
            # 检查SSH连接是否稳定
            if os.environ.get('SSH_CLIENT'):
                # 可以添加网络连接检查
                pass
            
            # 检查父进程是否存在
            ppid = os.getppid()
            if not os.path.exists("/proc/{}".format(ppid)):
                return False
            
            return True
            
        except Exception:
            return False
    
    def get_privilege_recommendations(self) -> List[str]:
        """获取权限维持建议"""
        if not self.current_privilege:
            self.analyze_current_privilege()
        
        recommendations = []
        privilege = self.current_privilege
        
        if privilege.stability == PrivilegeStability.TEMPORARY:
            recommendations.append("当前为临时权限，建议建立持久化后门")
            recommendations.append("考虑使用定时任务或服务方式维持权限")
        
        if privilege.stability == PrivilegeStability.SESSION_BASED:
            recommendations.append("当前为会话权限，建议建立独立于会话的后门")
            recommendations.append("考虑使用系统服务或内核模块方式")
        
        if privilege.privilege_type == PrivilegeType.SUDO:
            recommendations.append("当前为sudo权限，建议获取真正的root权限")
            recommendations.append("可以尝试利用sudo配置漏洞提权")
        
        if privilege.expiry_time:
            remaining = privilege.expiry_time - time.time()
            if remaining < 600:  # 10分钟
                recommendations.append("权限将在 {} 秒后过期，请尽快建立后门".format(int(remaining)))
        
        return recommendations
    
    def monitor_privilege_changes(self, callback=None) -> None:
        """监控权限变化"""
        self.logger.info("开始监控权限变化...")
        
        last_privilege = self.current_privilege
        
        while True:
            try:
                time.sleep(30)  # 每30秒检查一次
                
                current_privilege = self.analyze_current_privilege()
                
                if last_privilege and self._privilege_changed(last_privilege, current_privilege):
                    self.logger.warning("检测到权限变化")
                    
                    if callback:
                        callback(last_privilege, current_privilege)
                
                last_privilege = current_privilege
                
            except KeyboardInterrupt:
                self.logger.info("权限监控已停止")
                break
            except Exception as e:
                self.logger.error("权限监控错误: {}".format(e))
                time.sleep(60)  # 错误后等待更长时间
    
    def _privilege_changed(self, old: PrivilegeInfo, new: PrivilegeInfo) -> bool:
        """检查权限是否发生变化"""
        return (old.uid != new.uid or 
                old.privilege_type != new.privilege_type or
                old.stability != new.stability)
    
    def export_privilege_info(self, filepath: str) -> None:
        """导出权限信息"""
        if not self.current_privilege:
            self.analyze_current_privilege()
        
        data = {
            'current_privilege': asdict(self.current_privilege),
            'privilege_history': [asdict(p) for p in self.privilege_history],
            'timestamp': time.time()
        }
        
        with open(filepath, 'w') as f:
            json.dump(data, f, indent=2, default=str)
        
        self.logger.info("权限信息已导出到: {}".format(filepath))
    

    
    def get_privilege_maintenance_recommendations(self) -> List[str]:
        """获取权限维持建议"""
        if not self.current_privilege:
            self.analyze_current_privilege()
        
        recommendations = []
        
        if self.current_privilege.privilege_type == PrivilegeType.ROOT:
            recommendations.extend([
                "建议创建隐藏的管理员账户作为后备",
                "设置定时任务维持权限",
                "配置SSH密钥认证",
                "安装rootkit工具"
            ])
        elif self.current_privilege.privilege_type == PrivilegeType.SUDO:
            recommendations.extend([
                "尝试提升到root权限",
                "利用sudo权限安装后门",
                "修改sudoers文件增加权限",
                "创建SUID程序"
            ])
        else:
            recommendations.extend([
                "寻找权限提升漏洞",
                "利用配置错误提权",
                "社会工程学获取凭据",
                "利用应用程序漏洞"
            ])
        
        # 根据稳定性添加建议
        if self.current_privilege.stability == PrivilegeStability.TEMPORARY:
            recommendations.append("当前权限不稳定，需要立即建立持久化机制")
        
        return recommendations

def main():
    """主函数 - 用于测试"""
    logging.basicConfig(level=logging.INFO)
    
    adapter = PrivilegeContextAdapter()
    
    print("=== 权限上下文分析 ===")
    privilege_info = adapter.analyze_current_privilege()
    
    print("用户: {} (UID: {})".format(privilege_info.username, privilege_info.uid))
    print("权限类型: {}".format(privilege_info.privilege_type.value))
    print("稳定性: {}".format(privilege_info.stability.value))
    print("组: {}".format(', '.join(privilege_info.groups)))
    print("权限来源: {}".format(privilege_info.source))
    
    if privilege_info.sudo_privileges:
        print("Sudo权限: {} 项".format(len(privilege_info.sudo_privileges)))
    
    if privilege_info.expiry_time:
        remaining = privilege_info.expiry_time - time.time()
        print("权限过期时间: {} 秒后".format(int(remaining)))
    
    print("\n=== 稳定性验证 ===")
    is_stable = adapter.verify_privilege_stability()
    print("权限稳定性: {}".format('稳定' if is_stable else '不稳定'))
    
    print("\n=== 维持建议 ===")
    recommendations = adapter.get_privilege_recommendations()
    for i, rec in enumerate(recommendations, 1):
        print("{}. {}".format(i, rec))

if __name__ == "__main__":
    main()